package com.cobra.minio.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cobra.minio.config.MinIoTemplate;
import com.cobra.minio.domain.MinioFile;
import com.cobra.minio.dto.ChunkUploadUrlDTO;
import com.cobra.minio.dto.UploadChunksDTO;
import com.cobra.minio.mapper.MinioMapper;
import com.cobra.minio.service.UploadService;
import com.cobra.minio.util.Result;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.minio.ListPartsResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author cobra
 * @date 2021年12月06日 9:44
 */
@Slf4j
@Api(tags = "minio")
@RestController
@RequestMapping("/minio")
public class MinioController {

    @Autowired
    MinIoTemplate minIoTemplate;

    @Autowired
    MinioMapper minioMapper;

    @Autowired
    UploadService uploadService;

    private static String PATH = "ceshi/";


    @ApiOperationSupport(order = 1)
    @ApiOperation(value = "(1) 获取已经上传的分片", tags = "minio")
    @GetMapping("/get_chunks")
    public Result<UploadChunksDTO> getChunks(String md5) throws Exception {
        log.info("md5值:{}查询已上传的文件记录", md5);
        //根据MD5值查询数据库是否有上传记录
        MinioFile minioFile = minioMapper.selectOne(new QueryWrapper<>(MinioFile.builder().md5(md5).build()));
        if (ObjectUtil.isEmpty(minioFile)) {
            return Result.success(UploadChunksDTO.builder().build());
        }

        UploadChunksDTO uploadChunksDTO = UploadChunksDTO.builder().uuid(minioFile.getUuid()).uploadId(minioFile.getUploadId()).build();
        if (ObjectUtil.isNotEmpty(minioFile) && !minioFile.getUploaded()) {
            uploadChunksDTO.setUploaded(Boolean.FALSE);
            //查询minio中已经存在的分片
            ListPartsResponse listPartsResponse = minIoTemplate.listMultipart(PATH + minioFile.getFileName(), minioFile.getUploadId());
            LinkedList<Integer> uploadParts = listPartsResponse.result().partList().stream().map(x -> x.partNumber()).collect(Collectors.toCollection(LinkedList::new));
            uploadChunksDTO.setChunks(uploadParts);
        } else {
            uploadChunksDTO.setUploaded(Boolean.TRUE);
        }
        log.info("md5值:{}查询已上传的文件记录,响应结果:{}", md5, JSONUtil.toJsonStr(uploadChunksDTO));
        return Result.success(uploadChunksDTO);
    }


    @ApiOperationSupport(order = 2)
    @ApiOperation(value = "(2) 新增文件上传记录", tags = "minio")
    @GetMapping("/add_upload_record")
    public Result<UploadChunksDTO> addUploadRecord(String md5, String fileName, Integer size, Integer totalChunks) throws Exception {
        log.info("md5值:{} 新增文件上传记录", md5);
        String filePath = PATH + fileName;
        //若文件进行了分片则需要先获取uploadId
        MinioFile minioFile = MinioFile.builder()
                .uuid(UUID.randomUUID().toString())
                .md5(md5)
                .fileName(fileName)
                .size(size)
                .filePath(filePath)
                .fileType(FileUtil.extName(fileName))
                .totalChunks(totalChunks)
                .uploaded(Boolean.FALSE)
                .build();

        if (totalChunks > 1) {
            String uploadId = minIoTemplate.getMultipartUploadId(filePath, null);
            minioFile.setUploadId(uploadId);
        }
        minioMapper.insert(minioFile);
        UploadChunksDTO uploadChunksDTO = UploadChunksDTO.builder().uploadId(minioFile.getUploadId()).uuid(minioFile.getUuid()).chunks(new LinkedList<>()).build();
        log.info("md5值:{} 新增文件响应数据:{}", md5, JSONUtil.toJsonStr(uploadChunksDTO));
        return Result.success(uploadChunksDTO);
    }

    @ApiOperationSupport(order = 3)
    @ApiOperation(value = "(3) 获取分片上传的url", tags = "minio")
    @GetMapping("/get_multipart_url")
    public Result<Map<String, String>> getMultipartUrl(String md5) {

        log.info("md5值:{} 获取文件上传地址", md5);
        MinioFile minioFile = minioMapper.selectOne(new QueryWrapper<>(MinioFile.builder().md5(md5).build()));
        if (ObjectUtil.isEmpty(minioFile)) {
            return Result.sysError();
        }
        Map<String, String> multipartUrl = uploadService.initMultiPartUploadUrl(PATH, minioFile.getFileName(), minioFile.getUploadId(), minioFile.getTotalChunks(), "");
        log.info("md5值:{} 获取文件上传地址响应数据:{}", md5, JSONUtil.toJsonStr(multipartUrl));
        return Result.success(multipartUrl);
    }


    @ApiOperationSupport(order = 3)
    @ApiOperation(value = "(3) 获取分片上传的url-v2", tags = "minio")
    @GetMapping("/get_multipart_url_v2")
    public Result<ChunkUploadUrlDTO> getMultipartUrlByChunkNumber(String md5, Integer partNumber) {

        log.info("md5值:{} 当前分片:{} 获取文件上传地址", md5, partNumber);
        MinioFile minioFile = minioMapper.selectOne(new QueryWrapper<>(MinioFile.builder().md5(md5).build()));
        if (ObjectUtil.isEmpty(minioFile)) {
            return Result.sysError();
        }
        ChunkUploadUrlDTO chunkUploadUrlDTO = uploadService.initMultiPartUploadUrlV2(PATH, minioFile.getFileName(), minioFile.getUploadId(), minioFile.getTotalChunks(), partNumber, "");
        log.info("md5值:{} 当前分片:{}  获取文件上传地址响应数据:{}", md5, partNumber, JSONUtil.toJsonStr(chunkUploadUrlDTO));
        return Result.success(chunkUploadUrlDTO);
    }


    @ApiOperationSupport(order = 4)
    @ApiOperation(value = "(4) 分片文件合并", tags = "minio")
    @GetMapping("/complete_multipart")
    @Transactional
    public Result completeMultipart(String md5) {
        log.info("md5值:{} 合并分片请求", md5);
        MinioFile minioFile = minioMapper.selectOne(new QueryWrapper<>(MinioFile.builder().md5(md5).build()));
        if (ObjectUtil.isEmpty(minioFile)) {
            return Result.sysError();
        }
        if (minioFile.getTotalChunks() != 1) {
            boolean merged = uploadService.mergeMultipartUpload(PATH + minioFile.getFileName(), minioFile.getUploadId());
            minioFile.setUploaded(merged);
        } else {
            minioFile.setUploaded(Boolean.TRUE);
        }
        minioMapper.updateById(minioFile);
        return Result.success();
    }


    @ApiOperationSupport(order = 5)
    @ApiOperation(value = "(5) 文件下载", tags = "minio")
    @GetMapping("/download/{uuid}")
    public void downloadFile(HttpServletResponse response, @PathVariable String uuid) throws Exception {
        log.info("执行文件的下载请求");
        MinioFile minioFile = minioMapper.selectById(uuid);
        if (ObjectUtil.isEmpty(minioFile)) {
            throw new RuntimeException("文件记录不存在");
        }
        String targetName = "cobra.mp4";
        minIoTemplate.downloadObject(PATH + minioFile.getFileName(), targetName, response);
    }
}
